package com.cl.code.database.mybatis.config;

import com.cl.code.database.mybatis.CoreMapper;
import com.cl.code.database.mybatis.module.sql.method.AbstractSqlMethod;
import com.cl.code.database.mybatis.module.sql.method.SqlMethod;
import com.cl.code.database.mybatis.module.sql.table.TableInfo;
import com.cl.code.database.mybatis.module.sql.table.TableInfoFactory;
import com.cl.code.database.mybatis.util.TextUtils;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.mapping.ResultSetType;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.mapping.StatementType;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.session.Configuration;
import org.springframework.core.GenericTypeResolver;
import org.springframework.util.Assert;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

/**
 * @author chengliang
 * @since 1.0.0
 */
public class CLMybatisMapperSelfBuilder {

    private final Configuration configuration;
    private final MapperBuilderAssistant assistant;
    private final Class<?> type;
    private final Class<?> coreMapperClass = CoreMapper.class;

    public CLMybatisMapperSelfBuilder(Configuration configuration, Class<?> type) {
        String resource = type.getName().replace('.', '/') + ".java (best guess)";
        this.assistant = new MapperBuilderAssistant(configuration, resource);
        this.configuration = configuration;
        this.type = type;
    }

    public void parse() {
        Class<?>[] classes = GenericTypeResolver.resolveTypeArguments(type, coreMapperClass);
        if (classes == null || classes.length == 0) {
            throw new RuntimeException("无法解析泛型参数");
        }
        Class<?> entityClass = classes[0];
        TableInfo tableInfo = TableInfoFactory.getTableInfo(entityClass);
        // 解析方法
        for (Method method : type.getMethods()) {
            if (!canHaveStatement(method)) {
                continue;
            }
            String mappedStatementId = type.getName() + "." + method.getName();
            // 过滤已被解析的方法
            if (configuration.hasStatement(mappedStatementId)) {
                continue;
            }
            // 过滤非CoreMapper的方法
            if (!coreMapperClass.equals(method.getDeclaringClass())) {
                continue;
            }

            SqlMethod sqlMethod;
            try {
                Class<?> sqlMethodClass = this.getClass().getClassLoader().loadClass("com.cl.code.database.mybatis.module.sql.method." + "CL" + TextUtils.firstUpperCase(method.getName()));
                if (!AbstractSqlMethod.class.isAssignableFrom(sqlMethodClass)) {
                    continue;
                }
                Constructor<?> constructor = sqlMethodClass.getConstructor(Class.class, Method.class, TableInfo.class, Configuration.class);
                Object o = constructor.newInstance(type, method, tableInfo, configuration);
                sqlMethod = (SqlMethod) o;

            } catch (Exception e) {
                continue;
            }

            Assert.notNull(sqlMethod, "sqlMethod is null");
            String mapperName = type.getName();
            assistant.setCurrentNamespace(mapperName);

            // 加入到
            Class<?> parameterType = sqlMethod.getParameterType();
            LanguageDriver lang = configuration.getDefaultScriptingLanguageInstance();
            SqlCommandType sqlCommandType = sqlMethod.getSqlCommandType();
            String id = method.getName();
            SqlSource sqlSource = sqlMethod.getSqlSource();
            StatementType statementType = StatementType.PREPARED;
            Integer fetchSize = null;
            Integer timeout = null;
            String parameterMap = null;
            String resultMap = null;
            Class<?> resultType = sqlMethod.getResultType();
            ResultSetType resultSetType = null;
            boolean flushCache = true;
            boolean useCache = false;
            boolean resultOrdered = false;
            KeyGenerator keyGenerator = NoKeyGenerator.INSTANCE;
            String keyProperty = null;
            String keyColumn = null;
            String databaseId = null;


            // 决定调用哪个方法
            String resultSets = null;
            boolean dirtySelect = false;

            assistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterType, resultMap, resultType,
                    resultSetType, flushCache, useCache, resultOrdered, keyGenerator, keyProperty, keyColumn, databaseId, lang, resultSets, dirtySelect);
        }
    }

    private static boolean canHaveStatement(Method method) {
        return !method.isBridge() && !method.isDefault();
    }


}
